﻿// (c) Comkarl Calipso
// By Matías Fidemraizer (http://www.matiasfidemraizer.com)
// -------------------------------------------------
// http://calipso.codeplex.com
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using Comkarl.Web.Compression;
using Comkarl.Web.Configuration;
using Comkarl.Web.Reflection;
using Comkarl.Web.Resources;
using Comkarl.Web.UI.Theming.Configuration;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Web;
using System.Web.Caching;
using System.Web.Configuration;
using System.Web.Hosting;
using System.Web.UI;
using System.Xml;

namespace Comkarl.Web.UI
{
    /// <summary>
    /// Represents a CSS manager, which handles includes, ScriptControl registration, and more.
    /// </summary>
    /// <remarks>
    /// <para>CssManager can be both a server control and a standalone class. But non-infrastructure code - this is ASP.NET Calipso-based projects - get
    /// instances of this class by accessing <see cref="Comkarl.Web.UI.Controls.ScriptControl.CssManager"/> property.</para>
    /// 
    /// <h3>Quick links</h3>
    /// <list type="bullet">
    ///     <item>
    ///         <a href="#concepts">1.0 Concepts</a>
    ///         <list type="bullet">
    ///             <item>
    ///                 <a href="#configuringCSSmanager">1.1 Configuring CssManager</a>
    ///             
    ///                 <list type="bullet">
    ///                     <item><a href="#runtimeincludes">1.1.0 Adding includes during run-time in ScriptControl-derived classes</a></item>
    ///                 </list>
    ///             </item>
    ///         </list>
    ///     </item>
    ///     <item>
    ///         <a href="#workingwithCSSmanager">2.0 Working with CssManager</a>
    ///         <list type="bullet">
    ///             <item>
    ///                 <a href="#cssreferences">2.1 CSS references</a>
    ///                 
    ///                 <list type="bullet">
    ///                     <item><a href="#perpagecssmanager">2.1.0     Per-page CssManager: the way to go</a></item>
    ///                     <item><a href="#standalonereferences">2.1.1     Standalone references</a></item>
    ///                     <item><a href="#cssmanagermodes">2.1.2   CssManager debug and release mode: combination and minification</a></item>
    ///                     <item><a href="#referenceincludeorder">2.1.3    Order of CSS stylesheet inclusion in rendered pages</a></item>
    ///                 </list>
    ///             </item>
    ///             <item><a href="#overriddingincludes">2.2    Overridding includes</a></item>
    ///         </list>
    ///     </item>
    ///     <item>
    ///         <a href="#theming">3.0  User interface theming</a>
    ///         
    ///         <list type="bullet">
    ///             <item><a href="#themingconcepts">3.1     Theming concepts</a></item>
    ///             <item>
    ///                 <a href="#configuringuitheming">3.2     Configuring UI theming</a>
    ///                 
    ///                 <list type="bullet">
    ///                     <item><a href="#commonincludes">3.2.1   Common includes</a></item>
    ///                     <item><a href="#commoncombinationgroup">3.2.2   Common combination groups</a></item>
    ///                     <item><a href="#configuringtheme">3.2.3   Configuring a theme</a></item>
    ///                 </list>
    ///             </item>
    ///             <item><a href="#thememasterpage">3.3     Theme-specific master pages</a></item>
    ///             <item><a href="#hotswitchtheme">3.4     Hot-switching themes during run-time</a></item>
    ///         </list>
    ///     </item>
    /// </list>
    /// 
    /// <a name="concepts" />
    /// <h3>1.0 Concepts</h3>
    /// <para>
    /// There are some concepts that should be taken in account about CssManager. In fact, these defines a lot of things that will be important in order to
    /// understand how it works. 
    /// 
    /// <list type="table">
    ///     <listheader>
    ///         <term>Concept</term>
    ///         <description>Explanation</description>
    ///     </listheader>
    ///     <item>
    ///         <term>Script control</term>
    ///         <description>
    ///             An script control is a regular server control (i.e. WebControl-derived class) that implements a set of new features that allow them to have a client-side Web browser counterpart.
    ///         </description>
    ///     </item>
    ///     <item>
    ///         <term>Include</term>
    ///         <description>
    ///             An include is a pointer to some file. It may point to a physical file stored in the server file system, a file embedded in some .NET assembly or in a remote site by URL.
    ///             Although these are configured, this does not mean that are going to be added to Web pages managed by ASP.NET application. Includes are just there to be referenced.
    ///         </description>
    ///     </item>
    ///     <item>
    ///         <term>Reference</term>
    ///         <description>
    ///            A reference is a pointer to an existing include. Referencing an include means adding a <c><script></script></c> HTML element to the rendered page.
    ///         </description>
    ///     </item>
    ///     <item>
    ///         <term>Cache</term>
    ///         <description>
    ///             CssManager manages CSS file dependencies. In order to optimize access to some of them, it builds a file cache in the server's file system.
    ///         </description>
    ///     </item>
    /// </list>
    /// </para>
    /// <br />
    /// <br />
    /// <a name="configuringCSSmanager" />
    /// <h4>1.1 Configuring CssManager</h4>
    /// 
    /// There are three configuration points for CssManager:
    /// <list type="bullet">
    ///     <item><description>Calispo configuration section on Web.config (usually it is an standalone file called "Calipso.config" in the ASP.NET application root)</description></item>
    ///     <item><description>Pages and master pages</description></item>
    ///     <item><description>Script control server code</description></item>
    /// </list>
    /// 
    /// <a name="configsection" />
    /// <h5>1.1.0 Calipso configuration section on Web.config</h5>
    /// 
    /// <para>
    /// First of all, locate <strong>Calipso.config</strong> configuration file in the root of ASP.NET application (if this file is not there, means that ASP.NET Calipso has been
    /// installed manually without NuGet package). 
    /// </para>
    /// 
    /// <para>
    /// <strong>Calipso.config</strong> has an XML configuration element called <strong>cssManager</strong> (i.e. <c><cssManager></c>) which is the one that can configure settings
    /// for later CssManager execution and build-time actions.
    /// </para>
    /// 
    /// <para>
    /// <c><cssManager></c> element has these mandatory attributes:
    /// <list type="table">
    ///     <listheader>
    ///         <term>Attribute</term>
    ///         <description>Description</description>
    ///     </listheader>
    ///     <item>
    ///          <term>cachePath</term>
    ///          <description>Defines a relative virtual path where cached, CssManager-generated script files will be stored in. Default is "~/cache".</description>
    ///     </item>
    ///     <item>
    ///         <term>cacheVersion</term>
    ///         <description>Sets a version number that will be appended to all cached script files in order to allow client-side script cache to be broken. Default is "1.0.0.0".</description>
    ///     </item>
    /// </para>
    /// 
    /// <para>
    /// <c><cssManager></c> XML element supports a child one: <c><includes></includes></c>. Includes, as said before in "Concepts", are pointers to files, either physical, embedded into .NET assemblies or remote sites (URLs). 
    /// 
    /// In order to add an include, an <c><add /></c> child XML element must be added to parent <c><includes></c>. 
    /// 
    /// <example>
    ///     <h6>A sample include of file kind (physical file).</h6>
    ///     <para>Note that file kind requires a relative path, starting from the ASP.NET application root:</para>
    ///     <code>
    ///         <add id="SomePrefix//MyPhysicalFile" source="/Styles/MyPhysicalFile.css" kind="File" />
    ///     </code>
    /// </example>
    /// 
    /// <example>
    ///     <h6>A sample include of resource kind (an embedded resource into some .NET assembly)</h6>
    ///     <para>Embedded resources source is a full assembly qualified name of the embedded file. That is, the full path to the embedded file comma assembly name:</para>
    ///     <code>
    ///         <add id="SomePrefix//MyEmbeddedFile" source="Samples.Styles.MyEmbeddedFile.css, Samples" kind="Resource" />
    ///     </code>
    /// </example>
    /// 
    /// <example>
    ///     <h6>A sample include of ExternalUrl kind (a remotely-hosted file referenced by URL)</h6>
    ///     <para>ExternalUrl-kind incldues are provided by specifying an URL to a remote site:</para>
    ///     <code>
    ///         <add id="SomePrefix//MyRemoteFile" source="http://somedomain.com/myremotefile.css" kind="ExternalUrl" />
    ///     </code>
    /// </example>
    /// </para>
    /// 
    /// <a name="runtimeincludes" />
    /// <h5>1.1.1 Adding includes during run-time in ScriptControl-derived classes</h5>
    /// <para>
    /// Includes can be added during run-time by calling <see cref="RegisterInclude"/> method during script controls' life-cycle. Recommended: register includes during
    /// <strong>Init</strong> or <strong>Load</strong> ASP.NET control event invokation.
    /// 
    /// <para>ScriptControl-derived classes can access its assigned CssManager instance by accessing <strong>CssManager</strong> property.</para>
    /// 
    /// <example>
    ///     <h6>A sample include of file kind</h6>
    ///     <para>Note that file kind requires a relative path, starting from the ASP.NET application root:</para>
    ///     <code>
    ///         CssManager.RegisterInclude("SomePrefix//MyPhysicalFile", "/Styles/MyPhysicalFile.css", Comkarl.Web.Resources.ResourceKind.File);
    ///     </code>
    /// </example>
    /// 
    /// <example>
    ///     <h6>A sample include of resource kind (an embedded resource into some .NET assembly)</h6>
    ///     <para>Embedded resources source is a full assembly qualified name of the embedded file. That is, the full path to the embedded file comma assembly name:</para>
    ///     <code>
    ///         CssManager.RegisterInclude("SomePrefix//MyEmbeddedFile", "Samples.Styles.MyEmbeddedFile.css, Samples", Comkarl.Web.Resources.ResourceKind.Resource);
    ///     </code>
    /// </example>
    /// 
    /// <example>
    ///     <h6>A sample include of ExternalUrl kind (a remotely-hosted file referenced by URL)</h6>
    ///     <para>ExternalUrl-kind incldues are provided by specifying an URL to a remote site:</para>
    ///     <code>
    ///         CssManager.RegisterInclude("SomePrefix//MyRemoteFile", "http://somedomain.com/myremotefile.css", Comkarl.Web.Resources.ResourceKind.ExternalUrl);
    ///     </code>
    /// </example>
    /// </para>
    /// 
    /// <br />
    /// <br />
    /// <a name="workingwithCSSmanager" />
    /// <h4>2.0 Working with CssManager</h4>
    /// <para>
    /// CssManager manages dependencies by adding includes. But includes are just pointers. One of main advantages of using ASP.NET Calipso is that it handles the task of adding
    /// the necessary <c><link /></c> HTML elements. This is achieved thanks to the <strong>reference system</strong>.
    /// </para>
    /// <para>
    /// Once an include is loaded, any object having access to some CssManager can reference the include itself. <strong>The act of referencing an include is telling the CssManager
    /// to add a <c><link /></c> tag into the rendered HTML page</strong>. In fact, then every control, master page or page that references an include, it does by an identifier rather than giving the
    /// absolute file name, and dependencies location can be changed overtime without affecting the code base but by just modifying ASP.NET Calipso XML configuration or changing the code that registers
    /// an include during run-time.
    /// </para>
    /// <para>
    /// In addition to above described advantage, ASP.NET Calipso handles dependencies in terms of combining and minifying them in order to reduce Web sites' load time and bandwith usage.
    /// </para>
    /// <para>
    /// Once CssManager is configured, since it is a regular control, the next step is adding it to a Master Page or a regular page. CssManager is located in the 
    /// <strong>Comkarl.Web.UI namespace</strong>. As NuGet package register all ASP.NET Calipso control namespaces in the Web.config, it can be used anywhere in any Master Page or regular page
    /// by just coding a server <c><calipso:CssManager runat="server" /></c> tag. 
    /// 
    /// CssManager server control requires some mandatory attributes:
    /// 
    /// <list type="table">
    ///     <listheader><term>Attribute</term><description>Description</description></listheader>
    ///     <item>
    ///         <term>ID</term>
    ///         <description>An unique identifier for the control like any other ASP.NET server control.</description>
    ///     </item>
    ///     <item>
    ///         <term>CombinedFileName</term>
    ///         <description>When CssManager works with <c><compilation debug="false" /></c> (release mode), it combines its CSS references. This attribute
    ///         defines the name of the file that combines such CSS references.</description>
    ///     </item>
    /// </list>
    /// 
    /// Other detail is CssManager control can be appended to any part of a page: header, body... <c><script /></c> tags will be rendered in the place where CssManager server control was
    /// declared.
    /// </para>
    /// 
    /// <a name="CSSreferences" />
    /// <h5>2.1 CSS references: design-time and run-time approach</h5>
    /// <para>
    /// There are two ways of adding CSS references: either in design-time or run-time. Actually, both are handled in the same way, but each have different use cases.
    /// </para>
    /// <h6>How to add design-time references</h6>
    /// <para>
    /// Design-time references are that ones added by a Master Page or regular page in their server markup. In order to add a reference, CssManager control has a child element called
    /// <strong>References</strong> which allow children elements <c><calipso:ResourceReference RefId="[Identifier of some loaded include]" /></c>.
    /// </para>
    /// <example>
    /// Next code listing would be a sample of how to add a reference:
    /// 
    /// <code>
    /// <calipso:CssManager ID="cssManager1" CombinedFileName="styles1.css" runat="server">
    ///     <References>
    ///         <calipso:ResourceReference RefId="SomePrefix//MyFile" />
    ///     </References>
    /// </calipso:CssManager>
    /// </code>
    /// 
    /// <para>Also note that references are kind-agnostic. That is, a reference can point to any kind of include (File, Resource or ExternalUrl) and CssManager handles the operations needed to
    /// correctly add the <c>LINK</c> tag to the rendered page.</para>
    /// </example>
    /// <para>Usually, design-time references are that ones that are widely-required by any script control or anything in the Web site.</para>
    /// 
    /// <h6>How to add run-time references</h6>
    /// <para>Run-time references are added by calling <see cref="RegisterReference"/> method. The method takes as unique argument the identifier of the include to reference. See <see cref="RegisterReference"/> definition
    /// for further information on this topic.</para>
    /// 
    /// <a name="perpagecssmanager" />
    /// <h5>2.1.0 Per-page CssManager: the way to go</h5>
    /// <para>
    /// In any Web site there are CSS files that are shared across all pages. For example, dependencies like <strong>jQuery UI stylesheets</strong>. In addition, there are others that are specific to some page: a grid widget that
    /// lists users in a user management page. It is understandable that all pages should not be adding a <c><link /></c> tag to include the grid widget stylesheets (CSS). Why other pages should increase their load time to parse an useless stylesheet file?
    /// </para>
    /// <para>CssManager control can be added as child of any control, but most of the times these are added in Master Pages and regular pages (that is, content pages).</para>
    /// <para>Most projects will always add a CssManager control to their master pages and those will add shared CSS references like common UI CSS rules, and other CssManager control to content pages or nested master pages, for more specific stylesheets.</para>
    /// <para>
    /// Doing so allows CssManager to add dependencies specifically in the context that these are really required. In other words: the so-called grid widget stylesheet would be referenced in a CssManager hosted by the content page "UserManager.aspx" 
    /// rather than its Master Page.
    /// </para>
    /// <para>
    /// Everything explained above takes an special importance when ASP.NET Calipso is working in release mode (i.e. <c><comilation debug="false"/></c> in regular Web.config), because CSS files will be combined into a combined and minifyed file
    /// for each CssManager control instance. For example, master page's CSS files would be combined into "master.css" file and an user management page CSS files would be combined into "usermanager.css". <strong>This adds efficiency
    /// in terms of Web browser/client-side page load-time and Web caching.</strong>
    /// </para>
    /// 
    /// <a name="standalonereferences" />
    /// <h5>2.1.1 Standalone references</h5>
    /// <para>While combination and minification of CSS files may work for a lot of cases, there are some that may require combination exclusions.</para>
    /// <para>This are standalone references. It is a way of telling CssManager that some reference should not end combined when context is in release mode.</para>
    /// 
    /// <h6>Design-time standalone references</h6>
    /// <para>A design-time standalone reference is configured in the same way as a regular one, but adding <c>Standalone="true"</c> attribute to the declaration:</para>
    /// <example>
    ///     <code>
    ///         <calipso:CssManager ID="cssManager1" CombinedFileName="stylesheet1.css" runat="server">
    ///             <References>
    ///                 <calipso:ResourceReference RefId="SomePrefix//MyFile" Standalone="true" /> //<=== Here is the change!
    ///             </References>
    ///         </calipso:CssManager>
    ///     </code>
    /// </example>
    ///
    /// <h6>Run-time standalone references</h6>
    /// <para>A run-time standalone reference is configured in the same way as a regular one, but giving an additional argument to <see cref="RegisterReference" /> and its optional boolean argument <strong>standalone</strong>.</para>
    /// 
    /// <a name="cssmanagermodes" />
    /// <h5>2.1.2 CssManager debug and release mode: combination and minification</h5>
    /// <para>CssManager behaves differently depending on ASP.NET execution mode: debug or release.</para>
    /// <list type="table">
    /// <listheader>
    /// <term>Mode</term><description>Description</description>
    /// </listheader>
    /// <item>
    /// <term>Debug</term>
    /// <description>
    /// This mode is activated when regular Web.config has compilation in debug mode (i.e. <c><system.web><compilation debug="true"/></system.web></c>).
    /// Debug mode means that CssManager will not combine and minify CSS dependencies. <strong>IMPORTANT NOTE: Do not use this mode in production environments, since CSS file cache is regenerated in every HTTP request.</strong>
    /// </description>
    /// </item>
    /// <item>
    /// <term>Release</term>
    /// <description>
    /// This mode is activated when regular Web.config has compilation in release mode (i.e. <c><system.web><compilation debug="false"/></system.web></c>). 
    /// Debug mode means that CssManager will combine and minify CSS dependencies. <strong>Use this mode either in development or production, but note that once CSS files are cached, these are not re-cached anymore but if cached files are
    /// physically removed from the cache directory (i.e. ASP.NET Calipso configuration, "cachePath").</strong>
    /// </description>
    /// </item>
    /// </list>
    /// 
    /// <a name="referenceincludeorder" />
    /// <h5>2.1.3 Order of CSS stylesheet inclusion in rendered pages</h5>
    /// <para>CssManager renders <c><script></script></c> tags for each reference added to itself, either when added from a script control or in design-mode, and <strong>it fully-guarantees that it will render the whole <c><script></script></c>
    /// tag for each reference in addition order.</strong> <c><script></script></c> tags are added in order for each CssManager references.</para>
    /// 
    /// <a name="overridingincludes" />
    /// <h4>2.2     Overridding includes</h4>
    /// <para>In some situations, a very concrete requirements makes that, for example, the embedded CSS that comes with a script control library should be replaced by a custom one (note: this talks about an embedded one as a sample, it can be
    /// of any kind). In CssManager, who register first an include wins. Later tries to register the same include with the same identifier will not take any effect.</para>
    /// <para>Above explained behavior (first include to be registered, wins) can be used to override includes. <strong>The best place to do such override is Calipso.config and static includes</strong>, because these are which are registered first before
    /// any script control or any other tries to register some include.</para>
    /// <para>
    /// Overridding an include is as easy as add other include in <strong>Calipso.config</strong> JavaSriptManager's static includes <strong>with the exact identifier of the include to be overridden.</strong>
    /// </para>
    /// <para>For example, maybe a script control called "HelloWorld" registers an include and reference during run-time and its identifier is "Controls//HelloWorld". Editing <strong>Calipso.config</strong> and adding this include will be enough to override the original one:</para>
    /// <code>
    /// <add id="Controls//HelloWorld" source="/Styles/MyCustomizedHelloWorldControl.css" kind="File" />
    /// </code>
    /// 
    /// <br/><br/>
    /// 
    /// <a name="theming" />
    /// <h3>3.0     User interface theming (UI theming)</h3>
    /// <para>ASP.NET Calipso introduces a purely CSS-based theming approach. "Theming" means creating visual styles that can switched based on user settings or by other variables. In fact, UI theming allows Web applications to change their look and
    /// feel easly, and switch over any developed visual style in a very easy way.</para>
    /// <para>
    /// Conceptually, an ASP.NET Calipso theme is a set of includes to stylesheets (CSS files), images and other multimedia content.
    /// </para>
    /// 
    /// <h5>What UI theming cannot do</h5>
    /// <para>ASP.NET Calipso UI theming cannot do next things:</para>
    /// <list type="bullet">
    ///     <item>Transform or modify HTML</item>
    ///     <item>Auto-reference theme includes</item>
    /// </list>
    /// 
    /// <para><strong>Special attention the second point: ASP.NET Calipso cannot auto-reference theme includes.</strong> What is it? UI theming configures a set of includes that can switch their sources (source is the file path to some stylesheet), but
    /// ASP.NET Calipso will not add these includes automatically, because each Master Page, regular page or control requires specific CSS stylesheets and style rules, and it would extremly inefficient parsing every link to stylesheet if those have rules
    /// that are not going to be used in a particular Web page. 
    /// </para>
    /// <para>In other words, ASP.NET Calipso UI theming cares about handling UI theming dependencies, but developer must explicitly reference an UI theme or common include. This is achieved either in the CssManager design-mode (adding a reference to
    /// <c><References></References></c> element) or by doing it in server code inside a script control server-side code like regular CSS references:</para>
    /// <code>
    /// <calipso:CssManager ID="cssManager" CombineFile="some.css" runat="server">
    ///     <References>
    ///         <calipso:ResourceReference RefId="SomeThemeReferenceId" />
    ///     </References>
    /// </calipso:CssManager>
    /// </code>
    /// <para>Or:</para>
    /// <code>CssManager.RegisterReference("SomeThemeReferenceId");</code>
    /// 
    /// 
    /// <a name="themingconcepts" />
    /// <h4>3.1     Theming concepts</h4>
    /// <para>Here is a list of some concepts that should be taken in account. Some details may be are not understood at all at this point, but these are explained further in this document.</para>
    /// <list type="table">
    ///     <listheader><term>Name</term><description>Description</description></listheader>
    ///     <item>
    ///         <term>Common includes</term>
    ///         <description>Common includes are that ones that are not tied to a specific theme, but are not handled like regular includes in terms of how CssManager treats them. These are combined and/or minified
    ///         with regular includes configured in the <c><includes></includes></c> CssManager configuration element inside <strong>Calipso.config</strong>, but these are still available for script controls or CssManager 
    ///         like any include, meaning that they can be referenced anywhere. <strong>A common include must be always of file kind!</strong>
    ///         </description>
    ///     </item>
    ///     <item>
    ///         <term>Theme includes</term>
    ///         <description>Theme includes are ones tied to a concrete UI theme, but these are still available for script controls or CssManager 
    ///         like any include, meaning that they can be referenced anywhere. Main difference with "common includes" is that include identifiers are shared across
    ///         all themes, but CssManager changes their source when theme is switched to other. <strong>A theme include must be always of file kind!</strong>
    ///         </description>
    ///     </item>
    ///     <item>
    ///         <term>Combination group</term>
    ///         <description>UI theming supports combining includes into other one. Combination group have their own include identifier and components of the combination are includes specified by reference (include identifier).</description>
    ///     </item>
    /// </list>
    /// 
    /// <a name="configuringuitheming" />
    /// <h4>3.2     Configuring UI theming</h4>
    /// <para>UI theming is <strong>entirely</strong> configured inside <strong>Calipso.config</strong> (remember that this is in the root of ASP.NET application like regular Web.config file) configuration file. Theming cannot be configured during run-time, meaning that <strong>any change to UI theming will take effect
    /// next ASP.NET application start (change to Web.config, IIS application pool recycle, server reboot...).</strong></para>
    /// <para>UI theming configuration is a child of <c><cssManager></cssManager></c> XML configuration element:</para>
    /// <code>
    /// <?xml version="1.0" encoding="utf-8" ?>
    /// <calipso>
    ///     <components>
    ///         <component id="JsonSerializer" type="Comkarl.Web.Serialization.JsonSerializer, Comkarl.Web" implementation="Comkarl.Web.Serialization.DefaultJsonSerializer, Comkarl.Web"/>
    ///         <component id="CssCompressor" type="Comkarl.Web.Compression.TextCompressor, Comkarl.Web" implementation="Comkarl.Web.Compression.CssCompressor, Comkarl.Web"/>
    ///         <component id="JavaScriptCompressor" type="Comkarl.Web.Compression.TextCompressor, Comkarl.Web" implementation="Comkarl.Web.Compression.JavaScriptCompressor, Comkarl.Web"/>
    ///     </components>
    ///     
    ///     <javascriptManager cacheVersion="1.0.0.0" resourcePath="~\cache" cachePath="~\cache" dependencyConventionPrefix="Dependency//" resourceConventionPrefix="Resources//" afterLoadConventionPrefix="AfterLoad//">
    ///     </javascriptManager>
    ///     
    ///     <cssManager cachePath="~\cache" cacheVersion="1.0.0.0">
    ///         // Here is the so-called UI theming configuration element!
    ///         <theming basePath="Themes" cachePath="cache\themes" themeCookieName="Calipso.ThemeId"> 
    ///         </theming>
    ///     </cssManager>
    /// </code>
    /// </remarks>
    ///
    /// <para><c><theming></theming></c> XML configuration element has these mandatory attributes:</para>
    /// <list type="table">
    ///     <listheader><term>Name</term><description>Description</description></listheader>
    ///     <item>
    ///         <term><strong>basePath</strong></term>
    ///         <description>It is the relative base path of themes directory where each theme and their CSS stylesheets and other resources are stored in. <strong>It is relative to the 
    ///         ASP.NET application root directory path. Do not add a slash at the beginning of the relative path (i.e. <c>/Themes</c>. It must be just <c>Themes</c>).</strong></description>
    ///     </item>
    ///     <item>
    ///         <term><strong>cachePath</strong></term>
    ///         <description>
    ///          <strong>It specifies a relative path of ASP.NET application where are stored cached themes</strong>. UI themeing does not overwrite themes when does its own combination and minification process. In addition, since UI theming supports combining a common include reference with a specific theme one,
    ///         it cannot reuse original themes directory. For that reason, <strong>it must create a directory in the ASP.NET application in order to cache processed themes.</strong> This cache is generated in build-time, and it
    ///         can be regenerated by re-building the ASP.NET Visual Studio project. <strong>Do not add a slash at the beginning of the relative path (i.e. <c>/cache</c>. It must be just <c>cache</c>).</strong>
    ///         </description>
    ///     </item>
    ///     <item>
    ///         <term><strong>themeCookieName</strong></term>
    ///         <description>It is a camel-cased or hungarian-cased (or any other, it is absolutely arbitrary to the developer) identifier. UI Theming chooses which theme should be used based on appending a cookie to any HTTP request coming from
    ///         the Web browser, and this attribute configures and customizes the cookie name. The content of such cookie is discussed later.</description>
    ///     </item>
    /// </list>
    /// <a name="commonincludes" />
    /// <h5>3.2.1   Common includes</h5>
    /// <para>Common includes are ones that will be available for any theme and can be part of a common or theme-specific combination group. Everything "common" is configured with <c><common></common></c> element as child of <c><theming></theming></c> XML configuration element</para>
    /// <para>In addition, <c><common></common></c> configuration element has a mandatory attribute <strong>basePath</strong> which is a relative path to theming base path (i.e. <c><theming basePath="[some relative path]"></theming></c>).</para>
    /// <para><c><common></common></c> configuration elements supports two child elements:</para>
    /// <list type="table">
    ///     <listheader><term>Name</term><description>Description</description></listheader>
    ///     <item>
    ///         <term><c><includes></includes></c></term>
    ///         <description>Common includes. It supports child <c><add /></c> elements. Each one is like a regular include (i.e. <c><add id="SomeIdentifier" source="/Styles/Some.css" kind="File" /></c>).</description>
    ///     </item>
    ///     <item>
    ///         <term><c><combine></combine></c></term>
    ///         <description>Defines a set of combination groups (discussed later).</description>
    ///     </item>
    /// </list>
    /// 
    /// <a name="commoncombinationgroup" />
    /// <h5>3.2.2   Common combination groups</h6>
    /// <para>A combination group is a set of previously configured common includes combined and minified into a single file. Members of a combination group are specified by reference (full includes are not allowed!).</para>
    /// <para>The result of creating a combination group is a regular include that can be referenced by its identifier anywhere in any CssManager.</para>
    /// <para>Each combination group has these attributes:</para>
    /// <list type="table">
    ///     <listheader><term>Name</term><description>Description</description></listheader>
    ///     <item>
    ///         <term>id</term>
    ///         <description>
    ///         It is an unique identifier like a regular include identifier that will be used to reference the combination group as any other include.
    ///         </description>
    ///     </item>
    ///     <item>
    ///         <term>source</term>
    ///         <description>The file name with .css extension of the resulting CSS stylesheet file of combining group's references (f.e. <c>styles.css</c>).</description>
    ///     </item>
    ///     <item>
    ///         <term>version</term>
    ///         <description>
    ///         A version number that will be appended as suffix just before file extension (.css). For example, "1.0.0.0" for a combination group that generates a "styles.css" file would end in
    ///         "styles.1.0.0.0.css".
    ///         </description>
    ///     </item>
    /// </list>
    /// 
    /// <para>For example, first configure two common includes</para>
    /// <code>
    ///     <common basePath="common">
    ///         <includes>
    ///             <add id="Include1" source="Blah.css" kind="File" />
    ///             <add id="Include2" source="Blah2.css" kind="File" />
    ///         </includes>
    ///         <combine>
    ///         </combine>
    ///     </common>
    /// </code>
    /// <para>And later add a combination group, referencing both added includes:</para>
    /// <code>
    /// <common basePath="common">
    ///     <includes>
    ///         <add id="Include1" source="Blah.css" kind="File" />
    ///         <add id="Include2" source="Blah2.css" kind="File" />
    ///     </includes>
    ///     <combine>
    ///         <group id="Include1AndInclude2" source="BlahAndBlah2.css" version="1.0.0.0">
    ///             <add referenceId="Include1" />
    ///             <add referenceId="Include2" />
    ///         </group>
    ///     </combine>
    /// </common>
    /// </code>
    /// <para><strong>Note that themes and combination groups are generated in build-time. Visual Studio ASP.NET project must be re-built in order to reflect any change.</strong></para>
    /// <para>Finally, both includes and combination groups can be referenced by any CssManager:</para>
    /// <code>
    /// <calipso:CssManager ID="cssManager1" CombinedFileName="stylesheet1.css" runat="server">
    ///     <References>
    ///         <calipso:ResourceReference RefId="Include1" />
    ///         <calipso:ResourceReference RefId="Include2" />
    ///         <calipso:ResourceReference RefId="Include1AndInclude2" />
    ///     </References>
    /// </calipso:CssManager>
    /// </code>
    /// <a name="configuringtheme" />
    /// <h5>3.2.3   Configuring a theme</h5>
    /// <para>A theme is a set of includes and combination groups of CSS stylesheet files.</para>
    /// <para>In term of how a theme is represented in the file system, themes are just directories having CSS file stylesheets, images and others (this is explained later).</para>
    /// <para>Each theme <strong>must configure exactly the same includes and combination groups but stylesheets' rules can absolutely be different, changing the visual experiencie of the Web site.</strong> That is because switching
    /// from one theme to other is switching the include behind a reference (in other words: changing the target CSS stylesheet file), and a theme must have the same include identifier in order to make such change.</para>
    /// <para>Themes are represented by child <c><theme></theme></c> child element of <c><themes></themes></c> parent configuration element:</para>
    /// <code>
    /// <?xml version="1.0" encoding="utf-8" ?>
    /// <calipso>
    ///     <components>
    ///         <component id="JsonSerializer" type="Comkarl.Web.Serialization.JsonSerializer, Comkarl.Web" implementation="Comkarl.Web.Serialization.DefaultJsonSerializer, Comkarl.Web"/>
    ///         <component id="CssCompressor" type="Comkarl.Web.Compression.TextCompressor, Comkarl.Web" implementation="Comkarl.Web.Compression.CssCompressor, Comkarl.Web"/>
    ///         <component id="JavaScriptCompressor" type="Comkarl.Web.Compression.TextCompressor, Comkarl.Web" implementation="Comkarl.Web.Compression.JavaScriptCompressor, Comkarl.Web"/>
    ///     </components>
    ///     
    ///     <javascriptManager cacheVersion="1.0.0.0" resourcePath="~\cache" cachePath="~\cache" dependencyConventionPrefix="Dependency//" resourceConventionPrefix="Resources//" afterLoadConventionPrefix="AfterLoad//">
    ///     </javascriptManager>
    ///     
    ///     <cssManager cachePath="~\cache" cacheVersion="1.0.0.0">
    ///         <theming basePath="Themes" cachePath="cache\themes" themeCookieName="Calipso.ThemeId"> 
    ///             // NOTE THAT <themes> ELEMENT HAS A MANDATORY ATTRIBUTE TO SET THE DEFAULT THEME
    ///             <themes defaultTheme="Some">
    ///                 <theme id="Some" description="Some sample theme" basePath="some" version="1.0.0.0">
    ///                 </theme>
    ///             </themes>
    ///         </theming>
    ///     </cssManager>
    /// </code>
    /// 
    /// <c><theme></theme></c> element attributes explained:
    /// <list type="table">
    ///     <listheader><term>Name</term><description>Description</description></listheader>
    ///     <item>
    ///         <term><strong>id</strong></term>
    ///         <description>An unique identifier for the theme. Spaces are not allowed. A valid identifier would be "Default", "Elegant", "Green"...</description>
    ///     </item>
    ///     <item>
    ///         <term><strong>description</strong></term>
    ///         <description>A comment or text describing the theme. For example: "An elegant but yet simple look and feel".</description>
    ///     </item>
    ///     <item>
    ///         <term><strong>version</strong></term>
    ///         <description>A version number for the theme. When build-time actions creates the theming cache, each theme has its own directory like original location of them. In this process, ASP.NET Calipso
    ///         will append the version number to the theme directory name. For example, a theme directory called "Default" would end in "Default.1.0.0.0".</description>
    ///     </item>
    /// </list>
    /// 
    /// <para>A theme supports includes and combination groups exactly the same way as common includes (<a href="#commonincludes">follow this link to learn more about this topic</a>), but <strong>it has a special feature:
    /// theme combination groups can reference common includes. The rest works exactly the same way with some exception: theme includes do not support versioning.</strong> This limitation is because theme itself has a version
    /// and a theme is a "group of resources" which should work with the same parent version, which is stamped in cached theme directory name.</para>
    /// <para>See next code listing for a sample of a theme configuration:</para>
    /// <code>
    /// <common basePath="common">
    ///     <includes>
    ///         <add id="Include1" source="Blah.css" version="1.0.0.0" kind="File" />
    ///         <add id="Include2" source="Blah2.css" version="1.0.0.0" kind="File" />
    ///     </includes>
    ///     <combine>
    ///         <group id="Include1AndInclude2" source="BlahAndBlah2.css" version="1.0.0.0">
    ///             <add referenceId="Include1" />
    ///             <add referenceId="Include2" />
    ///         </group>
    ///     </combine>
    /// </common>
    /// <themes defaultTheme="Some">
    ///     <theme id="Some" description="Some sample theme" basePath="some" version="1.0.0.0">
    ///         <includes>
    ///             <add id="Include3" source="Blah3.css" kind="File" />
    ///             <add id="Include4" source="Blah4.css" kind="File" />
    ///         </includes>
    ///         <combine>
    ///             <group id="Include3AndInclude4" source="Blah3AndBlah4.css">
    ///                 <add referenceId="Include3" />
    ///                 <add referenceId="Include4" />
    ///             </group>
    ///         </combine>
    ///     </themes>
    /// </themes>
    /// </code>
    /// <para>Later theme identifiers can be used in any CssManager too like any other reference to some include:</para>
    /// <code>
    /// <calipso:CssManager ID="cssManager1" CombinedFileName="stylesheet1.css" runat="server">
    ///     <References>
    ///         <calipso:ResourceReference RefId="Include3" />
    ///         <calipso:ResourceReference RefId="Include4" />
    ///         <calipso:ResourceReference RefId="Include3AndInclude4" />
    ///     </References>
    /// </calipso:CssManager>
    /// </code>
    /// <para>Finally, additional themes are added as other <c><theme></theme></c> elements and with their own configuration, includes and combination groups.</para>
    /// 
    /// <a name="thememasterpage" />
    /// <h4>3.3     Theme-specific master pages</h4>
    /// <para>In ASP.NET Calipso's theming approach, a theme is a directory containing CSS files, images and other things. In specific: <strong>master pages.</strong></para>
    /// <para>If a theme directory contains a Master Page with the exact name of existing one, and same place holders like a root application one, ASP.NET Calipso will switch the root one with
    /// the theme-specific master page.</para>
    /// <para>Theme master pages <strong>are a good way of modifying Web site's look and feel more in depth.</strong></para>
    /// 
    /// <a name="hotswitchtheme" />
    /// <h4>3.4     Hot-switching themes during run-time</h4>
    /// <para>Once everything regarding theming configuration and CssManager references are spread acrosss pages, and there is more than a theme configured, next requirement is how to switch themes.</para>
    /// <para>ASP.NET Calipso UI theming configures a default theme in <strong>Calipso.config</strong> configuration file (i.e. <c><themes defaultTheme="SomeThemeIdentifier"></themes></c>).</para>
    /// <para>Default configured theme is overridden in a per-request basis. This is possible because ASP.NET Calipso configures an HTTP module that can receive an HTTP cookie with a configured theme identifier 
    /// (i.e. <theme id="Some" ...></theme>).</para>
    /// <para>
    /// The whole cookie must be called with the name that was previously configured in the UI theming <c><theming themeCookieName="Calipso.ThemeId"></theming></c>. If an HTTP request from the browser has an HTTP cookie
    /// called (for example) "Calipso.ThemeId" contaning the exact identifier of a configured theme (case insensitive), ASP.NET Calipso will render the appropiate CSS references for the selected theme. <strong>If no cookie
    /// is received, then default theme is selected.</strong>
    /// </para>
    /// <h5>How would a real-life ASP.NET application switch themes based on user settings?</h5>
    /// <para>A real-life ASP.NET application would store some user settings in a data store like a database (f.e. SQL Server), and it would store theme cookie when user authenticates against the server, meaning that
    /// all next HTTP requests will come with the whole theme cookie!</para>
    [ParseChildren(true)]
    public class CssManager : Comkarl.Web.UI.Controls.WebControl
    {
        /// <summary>
        /// Default constructor.
        /// </summary>
        public CssManager()
        {
            _references = new List<ResourceReference>();
        }

        static CssManager()
        {
            _dynamicIncludes = new List<IncludeElement>();
        }

        private readonly List<ResourceReference> _references;

        private static volatile List<IncludeElement> _dynamicIncludes;

        private static ReadOnlyCollection<IncludeElement> StaticIncludes
        {
            get
            {
                return new ReadOnlyCollection<IncludeElement>(CalipsoSection.Current.CssManager.Includes.Cast<IncludeElement>().ToList());
            }
        }

        internal static List<IncludeElement> DynamicIncludes
        {
            get
            {
                return _dynamicIncludes;
            }
        }

        private ReadOnlyCollection<IncludeElement> AllIncludes
        {
            get
            {
                List<IncludeElement> includes = new List<IncludeElement>();
                includes.AddRange(StaticIncludes);
                includes.AddRange(DynamicIncludes);
                includes.AddRange(ThemeIncludes);

                return new ReadOnlyCollection<IncludeElement>(includes);
            }
        }

        private ReadOnlyCollection<IncludeElement> ThemeIncludes
        {

            get
            {
                List<IncludeElement> includes = new List<IncludeElement>();

                if (ThemeCache != null && ThemeCache.Count > 0)
                {
                    if (ThemeCache.ContainsKey("Common"))
                    {
                        includes.AddRange(ThemeCache["Common"]);
                    }

                    if (ThemeCache.ContainsKey(CurrentThemeId))
                    {
                        includes.AddRange(ThemeCache[CurrentThemeId]);
                    }
                }

                return new ReadOnlyCollection<IncludeElement>(includes);
            }
        }

        private static IDictionary<string, IEnumerable<IncludeElement>> ThemeCache
        {
            get
            {
                if (HostingEnvironment.Cache["Calipso.CssManager.ThemeCache"] == null)
                {
                    HostingEnvironment.Cache.Add
                    (
                        "Calipso.CssManager.ThemeCache",
                        new Dictionary<string, IEnumerable<IncludeElement>>(),
                        null,
                        Cache.NoAbsoluteExpiration,
                        Cache.NoSlidingExpiration,
                        CacheItemPriority.High,
                        (key, value, reason) => { if (key == "Calipso.CssManager.ThemeCache") CreateThemeCache(); }
                    );
                };

                return (IDictionary<string, IEnumerable<IncludeElement>>)HostingEnvironment.Cache["Calipso.CssManager.ThemeCache"];
            }
        }

        /// <summary>
        ///  Gets current request theme identifier.
        /// </summary>
        public static string CurrentThemeId
        {
            get
            {
                if (HttpContext.Current.Items["CssManager.CurrentThemeId"] == null)
                {
                    CurrentThemeId = CalipsoSection.Current.CssManager.Theming.Themes.DefaultThemeId;
                }

                return (string)HttpContext.Current.Items["CssManager.CurrentThemeId"];
            }
            internal set
            {
                HttpContext.Current.Items["CssManager.CurrentThemeId"] = value;
            }
        }

        internal static bool ThemingEnabled
        {
            get
            {
                return CalipsoSection.Current.CssManager.Theming.Themes.Count > 0;
            }
        }

        /// <summary>
        /// Gets or sets combined file name. Combination and minification occurs when
        /// Web.config has <code><compilation debug="false" /></code>
        /// </summary>
        public string CombinedFileName
        {
            get;
            set;
        }

        /// <summary>
        /// Gets all CSS file references. Accumulates references specified by design-time added references (i.e. in the
        /// server markup - pages and master pages - and ones added by calling <see cref="RegisterReference"/> method).
        /// </summary>
        [PersistenceMode(PersistenceMode.InnerProperty)]
        public List<ResourceReference> References
        {
            get
            {
                return _references;
            }
        }

        private static List<string> AlreadyRegisteredReferences
        {
            get
            {
                if (HttpContext.Current.Items["CssManager.AlreadyRegisteredReferences"] == null)
                {
                    HttpContext.Current.Items.Add("CssManager.AlreadyRegisteredReferences", new List<string>());
                }

                return (List<string>)HttpContext.Current.Items["CssManager.AlreadyRegisteredReferences"];
            }
        }

        private static void WriteStyleTag(XmlWriter writer, string source)
        {
            writer.WriteStartElement("link");
            writer.WriteAttributeString("rel", "stylesheet");
            writer.WriteAttributeString("type", "text/css");
            writer.WriteAttributeString("href", source);
            writer.WriteFullEndElement();
        }

        private IncludeElement GetThemeInclude(string refId)
        {
            return ThemeIncludes.Single(include => include.Id == refId);
        }

        private void CombineReferences(XmlWriter xmlWriter)
        {
            List<IncludeElement> excludedIncludes = new List<IncludeElement>();

            bool combined = false;
            string combinedFilePath = FileNameUtility.ChangeVersionInName(Path.Combine(RuntimeEnvironment.CssCacheAbsolutePath, CombinedFileName), CalipsoSection.Current.CssManager.CacheVersion);

            if (!File.Exists(combinedFilePath))
            {
                using (FileStream stream = File.Create(combinedFilePath))
                using (StreamWriter writer = new StreamWriter(stream))
                {
                    IncludeElement tempInclude = null;

                    string tempVirtualPath = null;

                    foreach (ResourceReference reference in References.Where(reference => !reference.Standalone))
                    {
                        if (!IsThemeInclude(reference.RefId))
                        {
                            if (!AlreadyRegisteredReferences.Contains(reference.RefId))
                            {
                                tempInclude = AllIncludes.Single(include => include.Id == reference.RefId);

                                if (tempInclude.Kind != ResourceKind.ExternalUrl)
                                {
                                    tempVirtualPath = VirtualPathUtility.ToAppRelative(AllIncludes.Single(include => include.Id == reference.RefId).Source);

                                    if (tempVirtualPath.First() != '~')
                                    {
                                        tempVirtualPath = '~' + tempVirtualPath;
                                    }

                                    writer.WriteLine
                                    (
                                        File.ReadAllText
                                        (
                                            HostingEnvironment.MapPath(tempVirtualPath)
                                        )
                                    );
                                }

                                writer.WriteLine();
                                writer.WriteLine();
                            }
                        }
                        else
                        {
                            excludedIncludes.Add(GetThemeInclude(reference.RefId));
                        }
                    }
                }

                combined = true;

                File.WriteAllText(combinedFilePath, CssCompressor.Current.Compress(File.ReadAllText(combinedFilePath)));
            }

            WriteStyleTag(xmlWriter, VirtualPathUtility.Combine(RuntimeEnvironment.CssCacheRelativePath, FileNameUtility.ChangeVersionInName(CombinedFileName, CalipsoSection.Current.CssManager.CacheVersion)));
            xmlWriter.WriteRaw("\n");

            excludedIncludes.AddRange
            (
                References.Where(reference => AllIncludes.Single(some => some.Id == reference.RefId).Kind == ResourceKind.ExternalUrl || reference.Standalone)
                        .Select(reference => AllIncludes.Single(some => some.Id == reference.RefId))
            );

            if (!combined)
            {
                excludedIncludes.AddRange(References.Where(reference => IsThemeInclude(reference.RefId)).Select(reference => GetThemeInclude(reference.RefId)));
            }

            foreach (IncludeElement include in excludedIncludes)
            {
                WriteStyleTag(xmlWriter, include.Source);
                xmlWriter.WriteRaw("\n");
            }
        }

        private void SerializeEmbeddedInclude(IncludeElement include)
        {
            string resourcePath = include.Source.Substring(0, include.Source.IndexOf(','));
            string tempFilePath = Path.Combine(RuntimeEnvironment.CssCacheAbsolutePath, resourcePath);

            if (HttpContext.Current.IsDebuggingEnabled || !File.Exists(resourcePath))
            {
                string assemblyName = include.Source.Substring(include.Source.IndexOf(',') + 1);
                string cssText = Assembly.Load(assemblyName).GetEmbeddedResourceAsString(resourcePath);

                File.WriteAllText
                (
                    FileNameUtility.ChangeVersionInName(tempFilePath, CalipsoSection.Current.CssManager.CacheVersion),
                    cssText
                );
            }

            include.Source = VirtualPathUtility.Combine(RuntimeEnvironment.CssCacheRelativePath, FileNameUtility.ChangeVersionInName(resourcePath, CalipsoSection.Current.CssManager.CacheVersion));
        }

        protected override void RenderControl(XmlWriter writer)
        {
            base.RenderControl(writer);

            if (HttpContext.Current.IsDebuggingEnabled)
            {
                bool first = true;

                foreach (ResourceReference reference in References)
                {
                    if (!AlreadyRegisteredReferences.Contains(reference.RefId))
                    {
                        if (first)
                        {
                            writer.WriteRaw("\n");
                            first = false;
                        }

                        if (AllIncludes.Count(include => include.Id == reference.RefId) == 0)
                        {
                            throw new InvalidOperationException
                            (
                                string.Format("Trying to render a <link /> to the reference '{0}' but there is no include configured with this identifier", reference.RefId)
                            );
                        }

                        WriteStyleTag(writer, AllIncludes.Single(include => include.Id == reference.RefId).Source);
                        writer.WriteRaw("\n");

                        AlreadyRegisteredReferences.Add(reference.RefId);
                    }
                }
            }
            else
            {
                CombineReferences(writer);
            }
        }

        private bool IncludeExists(string id)
        {
            return AllIncludes.Count(include => include.Id == id) > 0 || (ThemingEnabled && (ThemeCache.ContainsKey("Common") && ThemeCache["Common"].Count(include => include.Id == id) > 0) || ThemeCache[CurrentThemeId].Count(include => include.Id != id) > 0);
        }

        private bool IsThemeInclude(string id)
        {
            return ThemingEnabled && ((ThemeCache.ContainsKey("Common") && ThemeCache["Common"].Count(include => include.Id == id) > 0) || ThemeCache[CurrentThemeId].Count(include => include.Id == id) > 0);
        }


        /// <summary>
        /// Registers an include to some CSS file, which can be a physical one hosted in ASP.NET application folder or subfolder, 
        /// an embedded file to some assembly or a file in some remote site referenced by an URL.
        /// </summary>
        /// <remarks>
        /// An include is a pointer to a CSS file. A file can be of these kinds (kind is specified by giving a value of <see cref="Comkarl.Web.Resources.ResourceKind"/> enumeration to
        /// <paramref name="kind"/> input parameter):
        /// <list>
        /// <item><b><see cref="Comkarl.Web.Resources.ResourceKind"/>.File</b></item>
        /// <item><b><see cref="Comkarl.Web.Resources.ResourceKind"/>.Resource</b></item>
        /// <item><b><see cref="Comkarl.Web.Resources.ResourceKind"/>.ExternalUrl</b></item>
        /// </list>
        /// 
        /// Depending on the kind, <paramref name="source"/> parameter sould be given with different formats:
        /// <list>
        ///     <item><term><b>File</b></term>. In this case, it should be an ASP.NET application relative path to the file. For example: /Styles/MyStyle.css</item>
        ///     <item><term><b>Resource</b></term>. Embedded resources should be specified by providing the full assembly qualified name of the resource. For example: "Path.To.MyStyle.css, MyAssemblyName"</item>
        ///     <item><term><b>ExternalUrl</b></term>External URLs are just plain URLs to a file in some remote site. For example: "http://domain.com/MyStyle.css"</item>
        /// </list>
        /// 
        /// Independtly to the file kind, any include must give an unique <paramref name="id"/>.
        /// </remarks>
        /// <param name="id">
        /// An unique identifier for the include (f.e. "MyApp//MyStyle" or just "MyStyle").
        /// </param>
        /// <param name="source">
        /// The source.
        /// </param>
        /// <param name="kind">
        /// The kind (see <see cref="Comkarl.Web.Resources.ResourceKind"/>).
        /// ></param>
        public void RegisterInclude(string id, string source, ResourceKind kind)
        {
            if (!IncludeExists(id))
            {
                IncludeElement include = new IncludeElement { Id = id, Source = source, Kind = kind };

                if (include.Kind == ResourceKind.Resource)
                {
                    SerializeEmbeddedInclude(include);
                }

                DynamicIncludes.Add(include);
            }
        }


        /// <summary>
        /// Registers a reference to some include.
        /// </summary>
        /// <exception cref="System.ArgumentOutOfRangeException">
        /// Thrown when <paramref name="id"/> does not correspond to an already registered include with the same identifier.
        /// </exception>
        /// <param name="id">
        /// The whole reference identifier to an already registered include.
        /// </param>
        /// <param name="standalone">
        /// Specifies if reference must be treated as standalone. 
        /// </param>
        public void RegisterReference(string id, bool standalone = false)
        {
            if (IsThemeInclude(id))
            {
                standalone = true;
            }

            if (IncludeExists(id))
            {
                References.Add(new ResourceReference { RefId = id, Standalone = standalone });
            }
            else
            {
                throw new ArgumentOutOfRangeException("id", id, "Cannot register this CSS reference: there is no include corresponding to given reference identifier");
            }
        }

        private static void CreateThemeCache()
        {
            ThemingElement theming = CalipsoSection.Current.CssManager.Theming;

            string appPath = HostingEnvironment.ApplicationVirtualPath;

            if (appPath.Last() != '/')
            {
                appPath += '/';
            }

            string themingCachePath = VirtualPathUtility.Combine(appPath, theming.CachePath);

            if (themingCachePath.Last() != '/' && themingCachePath.Last() != '\\')
            {
                themingCachePath += '/';
            }

            if (!string.IsNullOrEmpty(theming.Common.BasePath))
            {
                string commonCachePath = VirtualPathUtility.Combine(themingCachePath, theming.Common.BasePath);

                if (commonCachePath.Last() != '/' && commonCachePath.Last() != '\\')
                {
                    commonCachePath += '/';
                }

                List<IncludeElement> commonIncludes = new List<IncludeElement>(theming.Common.Includes.Cast<IncludeElement>());
                commonIncludes.AddRange(theming.Common.Combine.Cast<ReferenceGroupElement>().Select(group => new IncludeElement { Id = group.Id, Source = FileNameUtility.ChangeVersionInName(group.Source, group.Version), Kind = ResourceKind.File }));

                foreach (IncludeElement include in commonIncludes)
                {
                    if (include.Kind == ResourceKind.File)
                    {
                        include.Source = FileNameUtility.ChangeVersionInName(VirtualPathUtility.Combine(commonCachePath, include.Source.TrimStart('/').TrimStart('\\')), include.Version);
                    }
                }

                ThemeCache.Add("Common", new ReadOnlyCollection<IncludeElement>(commonIncludes));
            }

            List<IncludeElement> themeIncludes = null;

            string themePath = null;

            foreach (ThemeElement theme in CalipsoSection.Current.CssManager.Theming.Themes.Cast<ThemeElement>())
            {
                themePath = VirtualPathUtility.Combine(themingCachePath, theme.BasePath);

                if (!string.IsNullOrEmpty(theme.Version))
                {
                    themePath = FileNameUtility.ChangeVersionInName(themePath, theme.Version) + "/";
                }
                else
                {
                    themePath += "/";
                }

                themeIncludes = new List<IncludeElement>();

                foreach (IncludeElement themeInclude in theme.Includes)
                {
                    if (themeInclude.Kind == ResourceKind.File)
                    {
                        themeInclude.Source = VirtualPathUtility.Combine(themePath, themeInclude.Source);
                    }

                    themeIncludes.Add(themeInclude);
                }

                foreach (ReferenceGroupElement group in theme.Combine)
                {
                    group.Source = VirtualPathUtility.Combine(themePath, group.Source.TrimStart('/').TrimStart('\\'));
                    themeIncludes.Add(new IncludeElement { Id = group.Id, Source = group.Source, Kind = ResourceKind.File });
                }

                ThemeCache.Add(theme.Id, new ReadOnlyCollection<IncludeElement>(themeIncludes.ToList()));
                themeIncludes.Clear();
            }
        }

        internal static string GetThemeVirtualPath(string themeId)
        {
            ThemeElement theme = CalipsoSection.Current.CssManager.Theming.Themes.Cast<ThemeElement>().Single(one => one.Id == themeId);

            string basePath = theme.BasePath;

            if (basePath.Last() != '/' || basePath.Last() != '\\')
            {
                basePath += '/';
            }

            if (!string.IsNullOrEmpty(theme.Version))
            {
                int insertionIndex = -1;

                insertionIndex = basePath.LastIndexOfAny(new char[] { '/', '\\' });

                if (insertionIndex == -1)
                {
                    insertionIndex = basePath.Length - 1;
                }

                basePath = basePath.Insert(insertionIndex, theme.Version);
            }

            string cachePath = CalipsoSection.Current.CssManager.Theming.CachePath;

            if (cachePath.Last() != '/' || cachePath.Last() != '\\')
            {
                cachePath += '/';
            }

            return VirtualPathUtility.Combine(@"\" + cachePath, basePath);
        }

        internal void Start()
        {
            if (!Directory.Exists(RuntimeEnvironment.CssCacheAbsolutePath))
            {
                Directory.CreateDirectory(RuntimeEnvironment.CssCacheAbsolutePath);
            }

            if (CalipsoSection.Current.CssManager.Theming.Themes.Count > 0)
            {
                CreateThemeCache();
            }

            string tempFilePath = null;
            string tempVersionedFilePath = null;
            string tempVersion = null;

            foreach (IncludeElement include in ((CalipsoSection)WebConfigurationManager.GetSection("calipso")).CssManager.Includes)
            {
                switch (include.Kind)
                {
                    case ResourceKind.Resource:
                        SerializeEmbeddedInclude(include);
                        break;

                    case ResourceKind.File:
                        tempFilePath = (HostingEnvironment.ApplicationVirtualPath + include.Source).Replace("//", "/");

                        tempVersion = include.Version;

                        if (string.IsNullOrEmpty(tempVersion))
                        {
                            tempVersion = CalipsoSection.Current.CssManager.CacheVersion;
                        }

                        if (!string.IsNullOrEmpty(tempVersion))
                        {
                            tempVersionedFilePath = Path.Combine(RuntimeEnvironment.CssCacheAbsolutePath, Path.GetFileName(tempFilePath));
                            tempVersionedFilePath = FileNameUtility.ChangeVersionInName(tempVersionedFilePath, tempVersion);

                            if (File.Exists(tempVersionedFilePath))
                            {
                                File.Delete(tempVersionedFilePath);
                            }

                            File.Copy
                            (
                                HostingEnvironment.MapPath(tempFilePath),
                                tempVersionedFilePath
                            );

                            include.Source = VirtualPathUtility.Combine(RuntimeEnvironment.CssCacheRelativePath, Path.GetFileName(tempVersionedFilePath));
                        }
                        else
                        {
                            include.Source = tempFilePath;
                        }

                        break;
                }
            }
        }
    }
}